package com.atguigu.gmall.activity.receiver;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;

/**
 * 
 * date:2022/9/20 11:07
 * 描述：
 **/
@Component
public class SeckillReceiver {

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SeckillGoodsService seckillGoodsService;

    //  监听消息，数据初始化
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_1,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = {MqConst.ROUTING_TASK_1}
    ))
    public void importToRedis(Message message, Channel channel){
        try {
            //  将当天要秒杀的商品存储到缓存.
            //  select * from seckill_goods where  date_format(start_time,'%Y-%m-%d') = '2022-09-20';
            QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
            seckillGoodsQueryWrapper.eq("date_format(start_time,'%Y-%m-%d')", DateUtil.formatDate(new Date()));
            seckillGoodsQueryWrapper.eq("status","1");
            seckillGoodsQueryWrapper.gt("stock_count",0);
            List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(seckillGoodsQueryWrapper);

            //  分析当前数据类型： hash hset key field value
            if (!CollectionUtils.isEmpty(seckillGoodsList)){
                //  秒杀key
                String seckillKey = RedisConst.SECKILL_GOODS;

                //  循环：
                for (SeckillGoods seckillGoods : seckillGoodsList) {
                    //  如果缓存中有数据了，则需要停止添加.
                    Boolean exist = this.redisTemplate.opsForHash().hasKey(seckillKey, seckillGoods.getSkuId().toString());
                    //  exist = true;
                    if (exist){
                        //  停止. return break continue;
                        continue;
                    }
                    //  存储到缓存.
                    this.redisTemplate.opsForHash().put(seckillKey,seckillGoods.getSkuId().toString(),seckillGoods);

                    //  商品秒杀数量的存储. list 队列中    或 使用string incr/decr
                    //  stock_count = 10  list 先进先出  lpush key value
                    for (Integer i = 0; i < seckillGoods.getStockCount(); i++) {
                        //  seckill:stock:skuId skuId
                        String stockKey = RedisConst.SECKILL_STOCK_PREFIX+seckillGoods.getSkuId();
                        this.redisTemplate.opsForList().leftPush(stockKey,seckillGoods.getSkuId().toString());
                        //  lpop/rpop key;
                    }

                    //  在初始化的时候，向管道中 发送消息 skuId:1
                    this.redisTemplate.convertAndSend("seckillpush",seckillGoods.getSkuId()+":1");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //  手动确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

    }

    //  监听消息：预下单
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_SECKILL_USER,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_USER),
            key = {MqConst.ROUTING_SECKILL_USER}
    ))
    public void seckillOrderUser(UserRecode userRecode ,Message message, Channel channel){
        /*
        1.  判断状态位.
        2.  判断用户是否下过订单.
                setnx key value;
        3.  减库存.
                list 队列 rpop key
                出队失败： 说明没有库存，则需要通知其他兄弟节点，别秒杀！
                    publish seckillpush skuId:0;
        4.  将用户秒杀的商品等信息，保存到redis 中. SeckillGoods

        5.  更新redis - stockCount  ,mysql - stockCount
         */
        try {
            if (userRecode!=null){
                //  预下单保存数据
                seckillGoodsService.seckillUserOrder(userRecode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //  手动确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    //  监听秒杀减库存：
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_SECKILL_STOCK,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_STOCK),
            key = {MqConst.ROUTING_SECKILL_STOCK}
    ))
    public void updateStock(Long skuId,Message message,Channel channel){
        //  判断
        try {
            if (skuId!=null){
                //  减库存操作.
                this.seckillGoodsService.updateStock(skuId);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //  手动确认消费消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    //  清空缓存数据
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_18,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = {MqConst.ROUTING_TASK_18}
    ))
    public void clearData(Message message,Channel channel){
        try {
            //  查出要清空的数据.
            //  end_time 需要判断时分秒 ;  status = 1
            QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
            seckillGoodsQueryWrapper.eq("status",1);
            seckillGoodsQueryWrapper.le("end_time",new Date());
            List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(seckillGoodsQueryWrapper);
            //  循环遍历
            for (SeckillGoods seckillGoods : seckillGoodsList) {
                this.redisTemplate.delete(RedisConst.SECKILL_STOCK_PREFIX+seckillGoods.getSkuId());

                //  设置更新数据 有多个秒杀商品时 利用 主键更新
                //  seckillGoods.setStatus("2");
                //  this.seckillGoodsMapper.updateById(seckillGoods);
            }
            //  清空秒杀商品
            this.redisTemplate.delete(RedisConst.SECKILL_GOODS);
            //  有可能预下单数据也存在
            this.redisTemplate.delete(RedisConst.SECKILL_ORDERS);
            //  真正下单数据
            this.redisTemplate.delete(RedisConst.SECKILL_ORDERS_USERS);

            //  将数据库中的秒杀商品数据的状态更新为2
            SeckillGoods seckillGoods = new SeckillGoods();
            seckillGoods.setStatus("2");
            seckillGoods.setUpdateTime(new Date());

            //  根据上面检索条件进行更新.
            this.seckillGoodsMapper.update(seckillGoods,seckillGoodsQueryWrapper);

        } catch (Exception e) {
            e.printStackTrace();
        }
        //  手动确认消费消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
}